忍者刘米米

11. Properties

Properties

Property Class Structure Enum Global Local Observer
Stored varorlet varorlet - yes
Computed var var var var var
Lazy Stored var var - without lazy - no
inherited yes
nonoerridden no
Stored type static static static
Computed type class static static
change property - mutating mutating
  • if instance is let,property would not changed
  • Observer can be used for global or local variable

Stored Properties

Stored properties can be either variable stored properties or constant stored properties

1
2
3
4
5
6
7
8
9
10
11
12
13
struct FixedLengthRange {
var firstValue: Int
let length: Int
}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2

rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

// this will report error as length is constant
rangeOfThreeItems.length = 4

Stored Properties of Constant Structure Instances

When an instance of a value type is marked as a constant, so are all of its properties.

If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties:

1
2
3
4
5
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3

rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

Lazy Stored Properties

What is Lazy

  • A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.
  • You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.
  • If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DataImporter {
/*
DataImporter is a class to import data from an external file.
The class is assumed to take a non-trivial amount of time to initialize.
*/

var fileName = "data.txt"
// the DataImporter class would provide data importing functionality here
}

class DataManager {
lazy var importer = DataImporter()
var data = [String]()
// the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created

print(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"

When to use Lazy

  • when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete.
  • when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.
  • As above , It is possible for a DataManager instance to manage its data without ever importing data from a file

Stored Properties and Instance Variables

  • In Objective - C, it has properties and instance variables
  • In Swift, it unifies these concepts into a single property declaration

Computed Properties

  • do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
  • Declare computed properties as var
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center

square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"

Shorthand Setter Declaration

If a computed property’s setter does not define a name for the new value to be set, a default name of newValue is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct AlternativeRect {
var origin = Point()
var size = Size()

var center: Point {

get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}

set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}

Read-Only Computed Properties

A computed property with a getter but no setter is known as a read-only computed property.

You can simplify the declaration of a read-only computed property by removing the get keyword and its braces:

1
2
3
4
5
6
7
8
9
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Prints "the volume of fourByFiveByTwo is 40.0"

Property Observers

Property observers observe and respond to changes in a property’s value.

What is it for

  • stored properties
  • global or local variable
  • inherited property (whether stored or computed) by overriding the property within a subclass.

What is not for

  • not for lazy stored properties.
  • not for nonoverridden computed properties, as it can be done by computed property’s setter.

willSet

  • willSet is called just before the value is stored.
  • passed the new property value as a constant parameter.
  • specify a name for this parameter or default is newValue

didSet

  • didSet is called immediately after the new value is stored.
  • passed a constant parameter containing the old property value.
  • name the parameter or use the default parameter name of oldValue
  • can assign a value to a property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

Global and Local Variables

  • computing and observing properties are also available to global variables and local variables.
  • Global constants and variables are always computed lazily without lazy mark
  • Local constants and variables are never computed lazily.

Type Properties

Property var let default value
Stored type yes yes must
Computed type yes -
  • Type properties are useful for defining values that are universal to all instances of a particular type,
  • Stored type properties can be variables or constants.
  • Computed type properties are always declared as variable properties
  • Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.
  • Stored type properties are lazily initialized on their first access. They are guaranteed to be initialized only once, even when accessed by multiple threads simultaneously, and they do not need to be marked with the lazy modifier.

Type Property Syntax

  • In C and Objective-C, you define static constants and variables associated with a type as global static variables
  • In Swift, however, type properties are written as part of the type’s definition,
  • For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {

static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}

class var overrideableComputedTypeProperty: Int {
return 107
}
}

Querying and Setting Type Properties

Querying syntax

1
2
3
4
5
6
7
8
9
print(SomeStructure.storedTypeProperty)
// Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// cap the new audio level to the threshold level
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// store this as the new overall maximum input level
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"